package testutil

import (
	"context"
	"testing"
	"time"

	"xagent/internal/providers"
	"xagent/internal/streaming"
)

// MockLLMProvider implements providers.LLMProvider for testing
type MockLLMProvider struct {
	Name string
}

// NewMockLLMProvider creates a new mock LLM provider
func NewMockLLMProvider() *MockLLMProvider {
	return &MockLLMProvider{
		Name: "mock-llm",
	}
}

func (m *MockLLMProvider) GetName() string {
	return m.Name
}

func (m *MockLLMProvider) Complete(ctx context.Context, prompt string, options *providers.CompletionOptions) (string, error) {
	return "mock response", nil
}

func (m *MockLLMProvider) Chat(ctx context.Context, messages []providers.Message, options *providers.ChatOptions) (string, error) {
	return "mock chat response", nil
}

func (m *MockLLMProvider) Stream(ctx context.Context, prompt string, options *providers.CompletionOptions) (<-chan string, error) {
	ch := make(chan string, 1)
	go func() {
		defer close(ch)
		ch <- "mock streaming response"
	}()
	return ch, nil
}

func (m *MockLLMProvider) StreamChat(ctx context.Context, messages []providers.Message, options *providers.ChatOptions) (<-chan streaming.StreamResponse, error) {
	ch := make(chan streaming.StreamResponse, 1)
	go func() {
		defer close(ch)
		ch <- streaming.StreamResponse{
			Content: "mock streaming response",
			Done:    true,
		}
	}()
	return ch, nil
}

func (m *MockLLMProvider) StreamChatWithHandler(ctx context.Context, messages []providers.Message, options *providers.ChatOptions, handler streaming.StreamHandler) error {
	// Send a mock streaming event
	if err := handler.OnEvent(&streaming.StreamEvent{
		Type:    "message",
		Content: "mock streaming response",
		Done:    false,
	}); err != nil {
		return err
	}

	// Send completion event
	if err := handler.OnEvent(&streaming.StreamEvent{
		Type:    "message",
		Content: "",
		Done:    true,
	}); err != nil {
		return err
	}

	return handler.OnComplete(ctx)
}

func (m *MockLLMProvider) Embed(ctx context.Context, text string) ([]float32, error) {
	return []float32{0.1, 0.2, 0.3}, nil
}

// MockMetrics implements monitoring metrics for testing
type MockMetrics struct {
	values map[string]float64
}

func NewMockMetrics() *MockMetrics {
	return &MockMetrics{
		values: make(map[string]float64),
	}
}

func (m *MockMetrics) Inc(name string, labels map[string]string) {
	key := name
	for k, v := range labels {
		key += ":" + k + "=" + v
	}
	m.values[key]++
}

func (m *MockMetrics) GetValue(name string, labels map[string]string) float64 {
	key := name
	for k, v := range labels {
		key += ":" + k + "=" + v
	}
	return m.values[key]
}

// WaitForMetric waits for a metric to reach a target value
func WaitForMetric(t *testing.T, metrics *MockMetrics, name string, labels map[string]string, target float64, timeout time.Duration) {
	deadline := time.Now().Add(timeout)
	for time.Now().Before(deadline) {
		if metrics.GetValue(name, labels) == target {
			return
		}
		time.Sleep(100 * time.Millisecond)
	}
	t.Fatalf("timeout waiting for metric %s to reach %f", name, target)
}
